/*
Copyright (c) 2020-2022, Huawei Technologies Co., Ltd
 All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

* Description: 2D图表可视化插件数据界面类
*/
#include "curve_display.h"

#include <qwt_legend.h>
#include <qwt_symbol.h>
#include <qwt_plot_layout.h>
#include <qwt_text_label.h>
#include <qwt_scale_widget.h>
#include <qwt_legend_label.h>
#include <QScrollBar>
#include "common/utils.h"


namespace chart_plugin {
using rviz::ColorProperty;
using rviz::Config;
using rviz::EnumProperty;
using rviz::FloatProperty;
using rviz::IntProperty;
using rviz::Property;
using rviz::StatusProperty;
using rviz::StringProperty;

const int32_t FIRST_CURVE_COLOR_R = 0xF5;
const int32_t FIRST_CURVE_COLOR_G = 0xF2;
const int32_t FIRST_CURVE_COLOR_B = 0x2E;
const qreal FIRST_CURVE_COLOR_ALPHA = 0x01;

const int32_t SECOND_CURVE_COLOR_R = 0xFF;
const int32_t SECOND_CURVE_COLOR_G = 0x3B;
const int32_t SECOND_CURVE_COLOR_B = 0x3B;
const qreal SECOND_CURVE_COLOR_ALPHA = 0x01;

const int32_t COLOR_ALPHA_MIN = 0x00;
const int32_t COLOR_ALPHA_MAX = 0x01;

const qreal CURVE_PEN_WIDTH = 0x02;

const qreal CURVE_SYMBOL_PEN_WIDTH = 0x01;
const int32_t CURVE_SYMBOL_SIZE_WIDTH = 0x02;
const int32_t CURVE_SYMBOL_SIZE_HRIGHT = 0x02;

// 楷体
const QString QWTPLOT_TITLE_FONT_FAMILY = "楷体";
const int32_t QWTPLOT_TITLE_FONT_POINT_SIZE = 0x10;

const int32_t QWTPLOT_GRID_COLOR_R = 0x00;
const int32_t QWTPLOT_GRID_COLOR_G = 0x73;
const int32_t QWTPLOT_GRID_COLOR_B = 0x81;
const qreal QWTPLOT_GRID_PEN_WIDTH = 0x1;

const double QWTPLOT_AXIS_XBOTTOM_MIN = 0x00;
const double QWTPLOT_AXIS_XBOTTOM_MAX = 0x96;

const double QWTPLOT_AXIS_YLEFT_MIN = 0x00;
const double QWTPLOT_AXIS_YLEFT_MAX = 0x0A;

const int32_t LEGEND_WIDGETS_SIZE = 0x01;

const QString LEGEND_STYLE_SHEET = "color: rgb(255, 255, 255)";

enum CurveType {
    FIRST_CURVE,
    SECOND_CURVE,
};

void CurveDisplay::InitTitleProperty()
{
    qwtPlotTitleProperty = new StringProperty("Chart Title", "Curve Chart", "Chart title name property.",
        this, SLOT(UpdateChartTitle()), this);

    xAxisTitleProperty = new
        StringProperty("X Axis Title", "X", "X Axis title name property.", this, SLOT(UpdateXAxisTitle()), this);

    yAxisTitleProperty = new
        StringProperty("Y Axis Title", "Y", "Y Axis title name property.", this, SLOT(UpdateYAxisTitle()), this);
}

void CurveDisplay::InitFirstCurveProperty(EnumProperty &curveProperty)
{
    firstCurveTitleProperty = new StringProperty("First Curve Title", "First Curve",
        "First curve title property setting.", &curveProperty, SLOT(UpdateCurveTitle()), this);

    firstCurveColorProperty = new
        ColorProperty("First Curve Color", QColor(FIRST_CURVE_COLOR_R, FIRST_CURVE_COLOR_G, FIRST_CURVE_COLOR_B),
        "Color of the first Curve setting.", &curveProperty, SLOT(UpdateCurveColorAndAlpha()), this);

    firstCurveAlphaProperty = new FloatProperty("First Curve Alpha", FIRST_CURVE_COLOR_ALPHA,
        "First curve alpha property setting.", &curveProperty, SLOT(UpdateCurveColorAndAlpha()), this);

    firstCurveAlphaProperty->setMin(COLOR_ALPHA_MIN);
    firstCurveAlphaProperty->setMax(COLOR_ALPHA_MAX);
}

void CurveDisplay::InitSecondCurveProperty(EnumProperty &curveProperty)
{
    secondCurveTitleProperty = new StringProperty("Second Curve Title", "Second Curve",
        "Second curve title property setting.", &curveProperty, SLOT(UpdateCurveTitle()), this);

    secondCurveColorProperty = new
        ColorProperty("Second Curve Color", QColor(SECOND_CURVE_COLOR_R, SECOND_CURVE_COLOR_G, SECOND_CURVE_COLOR_B),
        "Color of the Second Curve setting.", &curveProperty, SLOT(UpdateCurveColorAndAlpha()), this);

    secondCurveAlphaProperty = new FloatProperty("Second Curve Alpha", SECOND_CURVE_COLOR_ALPHA,
        "Second Curve alpha property setting.", &curveProperty, SLOT(UpdateCurveColorAndAlpha()), this);

    secondCurveAlphaProperty->setMin(COLOR_ALPHA_MIN);
    secondCurveAlphaProperty->setMax(COLOR_ALPHA_MAX);
}

void CurveDisplay::InitCurveProperty()
{
    curveProperty = new
        EnumProperty("Curve", "First Curve", "Curve type setting selection.", this, SLOT(UpdateCurveChoice()));

    curveProperty->addOption("First Curve", FIRST_CURVE);
    curveProperty->addOption("Second Curve", SECOND_CURVE);

    InitFirstCurveProperty(*curveProperty);
    InitSecondCurveProperty(*curveProperty);
}

void CurveDisplay::InitProperty()
{
    InitTitleProperty();
    InitCurveProperty();
}

void CurveDisplay::UpdateCurveColorAndAlpha()
{
    QColor colorFirstCurve = firstCurveColorProperty->getColor();
    QColor colorSecondCurve = secondCurveColorProperty->getColor();
    qreal alphaFirstCurve = firstCurveAlphaProperty->getFloat();
    qreal alphaOptimizedCurve = secondCurveAlphaProperty->getFloat();
    colorFirstCurve.setAlpha(alphaFirstCurve * 0xFF);
    colorSecondCurve.setAlpha(alphaOptimizedCurve * 0xFF);
    firstCurve.setPen(colorFirstCurve, CURVE_PEN_WIDTH);
    secondCurve.setPen(colorSecondCurve, CURVE_PEN_WIDTH);

    QwtSymbol *firstSymbol = new QwtSymbol(QwtSymbol::Ellipse, QBrush(colorFirstCurve),
        QPen(colorFirstCurve, CURVE_SYMBOL_PEN_WIDTH), QSize(CURVE_SYMBOL_SIZE_WIDTH, CURVE_SYMBOL_SIZE_HRIGHT));

    firstCurve.setSymbol(firstSymbol);
    QwtSymbol *optimizedSymbol = new QwtSymbol(QwtSymbol::Ellipse, QBrush(colorSecondCurve),
        QPen(colorSecondCurve, CURVE_SYMBOL_PEN_WIDTH), QSize(CURVE_SYMBOL_SIZE_WIDTH, CURVE_SYMBOL_SIZE_HRIGHT));

    secondCurve.setSymbol(optimizedSymbol);
    ui.qwtPlot->replot();
}


void CurveDisplay::UpdateCurveTitle()
{
    QString firstCurveTitle = firstCurveTitleProperty->getString();
    QString secondCurveTitle = secondCurveTitleProperty->getString();

    firstCurve.setTitle(firstCurveTitle);
    secondCurve.setTitle(secondCurveTitle);
    ui.qwtPlot->replot();
}

void CurveDisplay::UpdateCurveChoice()
{
    bool isUseFirstStTrackCurve = true;
    bool isUseSecondStTrackCurve = false;
    if (curveProperty->getOptionInt() == SECOND_CURVE) {
        isUseFirstStTrackCurve = false;
        isUseSecondStTrackCurve = true;
    }

    firstCurveTitleProperty->setHidden(!isUseFirstStTrackCurve);
    firstCurveColorProperty->setHidden(!isUseFirstStTrackCurve);
    firstCurveAlphaProperty->setHidden(!isUseFirstStTrackCurve);
    secondCurveTitleProperty->setHidden(!isUseSecondStTrackCurve);
    secondCurveColorProperty->setHidden(!isUseSecondStTrackCurve);
    secondCurveAlphaProperty->setHidden(!isUseSecondStTrackCurve);
}

const QString CurveDisplay::StringElidedText(const QFont &font, const QString &text, const int32_t displayAreaWidth)
{
    const int32_t titleOffset = 80;
    QFontMetrics metrics(font);
    QString strText = text;
    int32_t textWidth = metrics.width(strText);
    if (displayAreaWidth <= (textWidth + titleOffset)) {
        strText = metrics.elidedText(strText, Qt::ElideRight, displayAreaWidth - titleOffset);
    }
    return strText;
}

void CurveDisplay::ShowCurve(QwtPlotItem *item, bool isVisible)
{
    if (item == nullptr) {
        return;
    }

    item->setVisible(isVisible);
    QwtLegend *legend = qobject_cast<QwtLegend *>(ui.qwtPlot->legend());
    QList<QWidget *> legendWidgets = legend->legendWidgets(ui.qwtPlot->itemToInfo(item));
    if (legendWidgets.size() == LEGEND_WIDGETS_SIZE) {
        QwtLegendLabel *legendLabel = qobject_cast<QwtLegendLabel *>(legendWidgets[0]);
        if (legendLabel != nullptr) {
            legendLabel->setChecked(isVisible);
        }
    }
}

void CurveDisplay::UpdateXAxisTitle()
{
    QwtScaleWidget *scaleWidget = ui.qwtPlot->axisWidget(QwtPlot::xBottom);
    int32_t xAxisWidth = scaleWidget->width();
    const int32_t xAxisWidthMin = 240;

    if (xAxisWidth < xAxisWidthMin) {
        xAxisWidth = xAxisWidthMin;
    }
    const QwtText scaleWidgetText = scaleWidget->title();
    QFont font = scaleWidgetText.font();

    QString text = xAxisTitleProperty->getString();
    scaleWidget->setToolTip(text);
    ui.qwtPlot->setAxisTitle(QwtPlot::xBottom, StringElidedText(font, text, xAxisWidth));
}

void CurveDisplay::UpdateYAxisTitle()
{
    QwtScaleWidget *scaleWidget = ui.qwtPlot->axisWidget(QwtPlot::yLeft);
    int32_t yAxisHeight = scaleWidget->height();
    const int32_t yAxisHeightMin = 100;

    if (yAxisHeight < yAxisHeightMin) {
        yAxisHeight = yAxisHeightMin;
    }
    const QwtText scaleWidgetText = scaleWidget->title();
    QFont font = scaleWidgetText.font();

    QString text = yAxisTitleProperty->getString();
    scaleWidget->setToolTip(text);
    ui.qwtPlot->setAxisTitle(QwtPlot::yLeft, StringElidedText(font, text, yAxisHeight));
}

void CurveDisplay::UpdateChartTitle()
{
    QwtScaleWidget *scaleWidget = ui.qwtPlot->axisWidget(QwtPlot::xBottom);
    int32_t xAxisWidth = scaleWidget->width();
    const int32_t xAxisWidthMin = 240;

    if (xAxisWidth < xAxisWidthMin) {
        xAxisWidth = xAxisWidthMin;
    }

    QwtTextLabel *titleLabel = (ui.qwtPlot)->titleLabel();
    const QwtText &qwtText = titleLabel->text();
    QFont font = qwtText.font();

    QString text = qwtPlotTitleProperty->getString();
    titleLabel->setToolTip(text);
    ui.qwtPlot->setTitle(StringElidedText(font, text, xAxisWidth));
}

void CurveDisplay::InitQwtPlotStyle()
{
    QwtText titleText;
    // 设置标题名
    titleText.setText(qwtPlotTitleProperty->getString());

    // 设置字体为楷体
    QFont textFont;
    textFont.setFamily(QWTPLOT_TITLE_FONT_FAMILY);
    textFont.setPointSize(QWTPLOT_TITLE_FONT_POINT_SIZE);
    // 设置粗体
    textFont.setBold(true);

    titleText.setFont(textFont);
    ui.qwtPlot->setTitle(titleText);
    // 设置图例的位置
    QwtLegend *legend = new QwtLegend();

    if (legend != nullptr) {
        legend->setStyleSheet(LEGEND_STYLE_SHEET);
        legend->setDefaultItemMode(QwtLegendData::Checkable);
        ui.qwtPlot->insertLegend(legend, QwtPlot::BottomLegend);
        connect(legend, &QwtLegend::checked, this, [=](const QVariant &itemInfo, bool isVisible, int){
            QwtPlotItem *plotItem = ui.qwtPlot->infoToItem(itemInfo);
            if (plotItem != nullptr) {
                plotItem->setVisible(isVisible);
            }
            ui.qwtPlot->replot();
        });
        legend->horizontalScrollBar()->setPalette(QPalette());
        legend->verticalScrollBar()->setPalette(QPalette());
    }

    // 绘制网格
    grid.setPen(QColor(QWTPLOT_GRID_COLOR_R, QWTPLOT_GRID_COLOR_G, QWTPLOT_GRID_COLOR_B), QWTPLOT_GRID_PEN_WIDTH);
    grid.attach(ui.qwtPlot);

    // 设置坐标轴 X的标题
    ui.qwtPlot->setAxisTitle(QwtPlot::xBottom, xAxisTitleProperty->getString());
    // X轴的范围
    ui.qwtPlot->setAxisScale(QwtPlot::xBottom, QWTPLOT_AXIS_XBOTTOM_MIN, QWTPLOT_AXIS_XBOTTOM_MAX);
    // Y轴名字
    ui.qwtPlot->setAxisTitle(QwtPlot::yLeft, yAxisTitleProperty->getString());
    // Y轴范围
    ui.qwtPlot->setAxisScale(QwtPlot::yLeft, QWTPLOT_AXIS_YLEFT_MIN, QWTPLOT_AXIS_YLEFT_MAX);
    ui.qwtPlot->setAxisAutoScale(QwtPlot::xBottom, true);
    ui.qwtPlot->setAxisAutoScale(QwtPlot::yLeft, true);
}

void CurveDisplay::InitCurveStyle(QwtPlotCurve &curvePlot, StringProperty &titleProperty,
    const ColorProperty &colorProperty, const FloatProperty &alphaProperty)
{
    // 图例名字
    curvePlot.setTitle(titleProperty.getString());

    QColor colorCurve = colorProperty.getColor();
    qreal alphaCurve = alphaProperty.getFloat();
    colorCurve.setAlpha(alphaCurve * 0xFF);
    // 设置画笔
    curvePlot.setPen(colorCurve, CURVE_PEN_WIDTH);

    // 设置反锯齿
    curvePlot.setRenderHint(QwtPlotItem::RenderAntialiased, true);

    // 显示图例的标志，这里显示线的颜色
    // 设置样本点的颜色、大小
    QwtSymbol *symbol = new QwtSymbol(QwtSymbol::Ellipse, QBrush(colorCurve),
        QPen(colorCurve, CURVE_SYMBOL_PEN_WIDTH), QSize(CURVE_SYMBOL_SIZE_WIDTH, CURVE_SYMBOL_SIZE_HRIGHT));

    // 添加样本点形状
    curvePlot.setSymbol(symbol);
    curvePlot.setLegendAttribute(curvePlot.LegendShowSymbol);
}

bool CurveDisplay::eventFilter(QObject *obj, QEvent *e)
{
    if (obj != &widget) {
        return QObject::eventFilter(obj, e);
    }

    if (e->type() != QEvent::Resize) {
        return QObject::eventFilter(obj, e);
    }

    UpdateChartTitle();
    UpdateXAxisTitle();
    UpdateYAxisTitle();
    return true;
}

void CurveDisplay::onInitialize()
{
    InitProperty();
    ChartDisplayClass::onInitialize();
    ui.SetupUi(widget);
    InitQwtPlotStyle();

    InitCurveStyle(firstCurve, *firstCurveTitleProperty, *firstCurveColorProperty, *firstCurveAlphaProperty);
    // 添加到控件上
    firstCurve.attach(ui.qwtPlot);
    ShowCurve(&firstCurve, true);

    InitCurveStyle(secondCurve, *secondCurveTitleProperty, *secondCurveColorProperty, *secondCurveAlphaProperty);
    // 添加到控件上
    secondCurve.attach(ui.qwtPlot);
    ShowCurve(&secondCurve, true);

    setAssociatedWidget(&widget);
    UpdateCurveChoice();
    widget.installEventFilter(this);
}

void CurveDisplay::onEnable()
{
    ChartDisplayClass::onEnable();

    UpdateChartTitle();
    UpdateXAxisTitle();
    UpdateYAxisTitle();
}

void CurveDisplay::ProcessMessage(const custom::PlotData::ConstPtr &message)
{
    if (message == nullptr) {
        setStatus(StatusProperty::Error, "Topic", "Topic Message is invalid!");
        return;
    }

    if (!curveData.Add(message)) {
        setStatus(StatusProperty::Error, "Topic", "Topic Message stSpaceData add failed!");
        return;
    }
    const QPolygonF firstCurvePolygon = curveData.GetFirstCurvePolyonF();
    const QPolygonF secondCurvePolygon = curveData.GetSecondCurvePolyonF();
    squareDifference = curveData.GetSquareDifference();
    ui.varianceValueLabel->setText(squareDifference);
    // 更新坐标轴
    firstCurve.setSamples(firstCurvePolygon);
    secondCurve.setSamples(secondCurvePolygon);
    ui.qwtPlot->replot();
}

void CurveDisplay::Clear()
{
    curveData.Clear();
    QPolygonF emptyPolyon;
    firstCurve.setSamples(emptyPolyon);
    secondCurve.setSamples(emptyPolyon);
    ui.varianceValueLabel->setText(QString());
    ui.qwtPlot->replot();
}

void CurveDisplay::reset()
{
    ChartDisplayClass::reset();
    curveData.Clear();
}

void CurveDisplay::updateTopic()
{
    Clear();
    ChartDisplayClass::updateTopic();
}
}

// 声明此类是一个rviz的插件
#include <pluginlib/class_list_macros.h>
PLUGINLIB_EXPORT_CLASS(chart_plugin::CurveDisplay, rviz::Display)
